﻿cls
#add sharepoint cmdlets
if ( (Get-PSSnapin -Name Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue) -eq $null )
{    
      Add-PsSnapin Microsoft.SharePoint.PowerShell
}


[System.Xml.XmlDocument] $XmlDoc = new-object System.Xml.XmlDocument
$file = resolve-path("C:\Sp2010profpowershell\Sp2010profpowershell\connections.xml")
if (!$file)
{
		Write-Host "Could not find the configuration file specified. Aborting." -ForegroundColor red	
		Break
}

write-host "Parsing file: " $file
$XmlDoc = [xml](Get-Content $file)
$ConnectionNodeList = $XmlDoc.Connections.Connection 

foreach ($ConnectionNode in $ConnectionNodeList) 
{
	if ($ConnectionNode)
	{

		$SyncConnectionName = $ConnectionNode.name
		Write-Host "Fetching Synchronization information for $SyncConnectionName." -ForegroundColor green
		#Fetching Sync Connection Information
    	$SyncSettings = $ConnectionNode.SelectSingleNode("SyncSettings")
	    $ProfileServiceApplicationName = $SyncSettings.SelectSingleNode("ProfileServiceApplicationName")
    	$ConnectionForestName = $SyncSettings.SelectSingleNode("ConnectionForestName")
	    $ConnectionDomain = $SyncSettings.SelectSingleNode("ConnectionDomain")
    	$ConnectionUserName = $SyncSettings.SelectSingleNode("ConnectionUserName")
	    $ConnectionPassword = $SyncSettings.SelectSingleNode("ConnectionPassword")
    	$ConnectionServerName = $SyncSettings.SelectSingleNode("ConnectionServerName")
	    $ConnectionSynchronizationOU = $SyncSettings.SelectSingleNode("ConnectionSynchronizationOU")
		
	    #############################################
    	#Doing some initial fail testing and cleanup#
	    #############################################
    	#Gettin User Profile Service Application
	    $UPA = Get-SPServiceApplication | where {$_.DisplayName -eq $ProfileServiceApplicationName.InnerText}
    	If(!$UPA)
	    {
    	    Write-Host "The User Profile Service application with name `" $ProfileServiceApplicationName `" does not exist. Cannot continue with domain: `"$ConnectionDomain`". Moving to next item." -ForegroundColor red
        	Continue
	    }
		
		$UPSS = Get-SPServiceInstance | where {$_.TypeName -eq "User Profile Synchronization Service" -and $_.Status -eq "Online"}
		if (!$UPSS)
		{
			Write-Host "There's no online User Profile Synchronization Service in the farm. Aborting." -ForegroundColor red
			Break
		}
    
    	#Trimming ConnectionForestName
	    $ConnectionForestName = $ConnectionForestName.InnerText

	    #Trimming ConnectionDomain
    	$ConnectionDomain = $ConnectionDomain.InnerText

	    #Checking the username format
    	$ConnectionUserName = $ConnectionUserName.InnerText
	    if (($ConnectionUserName.Contains("\")) -or (!$ConnectionUserName.Contains("@")))
    	{
        	Write-Host "Invalid Synchronization Account format. The format is: `"username@domain.com`". Cannot continue with domain: `"$ConnectionDomain`". Moving to next item." -ForegroundColor red
	        Continue
    	}
            
	    #Converting the password to secure string
    	$ConnectionPassword = $ConnectionPassword.InnerText
	    $ConnectionPassword = ConvertTo-SecureString -AsPlainText -String $ConnectionPassword -force
	
	    #Parsing ConnectionServerName
    	$ConnectionServerName = $ConnectionServerName.InnerText

	    #Making sure the Connection OU parameter meets RFC standards.
    	$ConnectionSynchronizationOU = $ConnectionSynchronizationOU.InnerText.ToUpper()

		Write-Host "Finished fetching Synchronization information for $SyncConnectionName." -ForegroundColor green

	    ##############################################
    	#/Doing some initial fail testing and cleanup#
	    ##############################################

    	######################################
	    #Creating or updating Sync Connection#
    	######################################    

		Write-Host "Creating user profile synchronizatoin connection $SyncConnectionName. (The connection will be created with display name $ConnectionServerName." -ForegroundColor green
	    Add-SPProfileSyncConnection -ProfileServiceApplication $UPA -ConnectionForestName $ConnectionForestName -ConnectionDomain $ConnectionDomain -ConnectionUserName $ConnectionUserName -ConnectionPassword $ConnectionPassword -ConnectionSynchronizationOU $ConnectionSynchronizationOU -ConnectionServerName $ConnectionServerName
		Write-Host "Finished creating user profile synchronizatoin connection." -ForegroundColor green

	    #######################################
    	#/Creating or updating Sync Connection#
	    #######################################

	    ##############################
    	#Preparing connection manager#
	    ##############################

	    $context = [Microsoft.Office.Server.ServerContext]::Default
		$UPAConfMgr = new-object Microsoft.Office.Server.UserProfiles.UserProfileConfigManager($context)
	    $UPAConnMgr = $UPAConfMgr.ConnectionManager
    	$userprofiletype = [Microsoft.Office.Server.UserProfiles.ProfileType]::User
	    $CurrentConnection = $UPAConnMgr | where {$_.AccountDomain -eq $ConnectionDomain}
    	$PropertyMapping = $CurrentConnection.PropertyMapping
		
		$userProfilePropertyManager = $UPAConfMgr.ProfilePropertyManager
		$userProfileTypeProperties = $userProfilePropertyManager.GetProfileTypeProperties($userprofiletype)
		
		#This section is required for creating new properties.
		$ServiceContextSite = $SyncSettings.SelectSingleNode("ServiceContextSite")
		#Creating core properties
		$CoreProperties = $UPAConfMgr.ProfilePropertyManager.GetCoreProperties()		
		
		

		###############################
	    #/Preparing connection manager#
    	###############################

	    #####################
    	#Creating properties#
	    #####################
		
		Write-Host "Creating user profile properties." -ForegroundColor green
		$PropertyNodeList = $XmlDoc.Connections.Connection.PropertyCreations
		
		#Basic check to see if there are entries to be created and the <ServiceContextSite> node 
		#is filled in. If not, we cannot continue.
		if ((!$ServiceContextSite.InnerText) -and ($PropertyNodeList))
		{
			Write-Host "There are entries in the <PropertyCreations> node in the parse file, but the <ServiceContextSite> node is empty. Critical error, cannot continue." -ForegroundColor red
			Break
		}
		
		$servicecontext = Get-SPServiceContext $ServiceContextSite.InnerText -ErrorAction SilentlyContinue
		
		#Checking if the servicecontext (the site specified in the <ServiceContextSite> attribute
		#in the parse file actually exist. If not, we cannot continue.
		if (!$servicecontext) 
		{
			Write-Host "There are entries in the <PropertyCreations> node in the parse file, but the <ServiceContextSite> node contains a URL to a site that does not exist. Critical error, cannot continue." -ForegroundColor red
			Break
		}
		$userProfileSubTypeManager = [Microsoft.Office.Server.UserProfiles.ProfileSubtypeManager]::Get($servicecontext)
		$userProfile = $userProfileSubTypeManager.GetProfileSubtype([Microsoft.Office.Server.UserProfiles.ProfileSubtypeManager]::GetDefaultProfileName($userprofiletype))
		$userProfileProperties = $userProfile.Properties
		
		foreach ($PropertyNode in $PropertyNodeList) 
	    {
		    if ($PropertyNode)
			{
				#Fetching information for the new property.
				$ProfilePropertyCreation = $PropertyNode.SelectSingleNode("ProfilePropertyCreation")
    	    	$SharePointProp = $ProfilePropertyCreation.InnerText
				$SharePointPropDisplayName = $ProfilePropertyCreation.DisplayName
				$SharePointPropPrivacy = $ProfilePropertyCreation.Privacy.ToLower()
				$SharePointPropPrivacyPolicy = $ProfilePropertyCreation.PrivacyPolicy.ToLower()
				$SharePointPropType = $ProfilePropertyCreation.PropType.ToLower()
				$SharePointPropLength = $ProfilePropertyCreation.PropLength
				$SharePointPropIsVisibleOnEditor = $ProfilePropertyCreation.IsVisibleOnEditor
				$SharePointPropIsVisibleOnViewer = $ProfilePropertyCreation.IsVisibleOnViewer
				$SharePointPropIsEventLog = $ProfilePropertyCreation.IsEventLog
				
				#Basic syntax checking of $SharePointPropPrivacy attribute
				$PrivacyMembers = "public", "contacts", "Organization", "Manager", "Private"
				if (!($PrivacyMembers -contains $SharePointPropPrivacy))
				{
					Write-Host "The new SharePoint property `"$SharePointProp`" contains an invalid privacy attribute ($SharePointPropPrivacy). The allowed entries are: public, contacts, Organization, Manager, Private. Skipping attribute." -ForegroundColor red
					Continue
				}
				
				#Basic syntax checking of $SharePointPropPrivacyPolicy attribute
				$PrivacyPolicyMembers = "mandatory", "optin","optout", "disabled"
				if (!($PrivacyPolicyMembers -contains $SharePointPropPrivacyPolicy))
				{
					Write-Host "The new SharePoint property `"$SharePointProp`" contains an invalid privacy policy attribute ($SharePointPropPrivacyPolicy). The allowed entries are: mandatory, optin,optout, disabled. Skipping attribute $SharePointProp." -ForegroundColor red
					Continue
				}
				
				#Basic syntax checking of $SharePointPropType attribute
				if ($SharePointPropType -ne "string")
				{
					Write-Host "Currently the script only able to create `"String`" type of attributes. The attribute provided is: $SharePointPropType. Skipping attribute $SharePointProp." -ForegroundColor red
					Continue
				}
				
				#Basic syntax checking of $SharePointPropLength attribute
				$loaderror = [Reflection.Assembly]::LoadWithPartialName("'Microsoft.VisualBasic")
				if ($loaderror -ne "Microsoft.VisualBasic, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")
				{
					$IsNumber = [Microsoft.VisualBasic.Information]::isnumeric($SharePointPropLength)
					
					if (!$IsNumber)
					{
						Write-Host "The property length specified for this attribute is invalid. Skipping attribute $SharePointProp." -ForegroundColor red
						Continue
					}
				}
				
				
				#Creating a NewProperty with basic attributes
				$NewProperty = $CoreProperties.Create($false)
				$newproperty.Name = $SharePointProp
				$NewProperty.DisplayName = $SharePointPropDisplayName
				$NewProperty.Type = $SharePointPropType
				$NewProperty.Length = $SharePointPropLength
				
				#Adding the new property to the core properties list
				$CoreProperties.Add($NewProperty)
				
				#Reinicializing the newly created property to change secondary attributes.
				$NewTypeProperty = $userProfileTypeProperties.Create($NewProperty)
				
				#Display attributes
				$NewTypeProperty.IsVisibleOnEditor = $SharePointPropIsVisibleOnEditor
				$NewTypeProperty.IsVisibleOnViewer = $SharePointPropIsVisibleOnViewer 
				$NewTypeProperty.IsEventLog = $SharePointPropIsEventLog
				
				#Updating the new property's secondary attributes
				$userProfileTypeProperties.Add($NewTypeProperty)
				
				#Reinicializing the newly created property to privacy attributes.
				$NewSubProperty = $userProfileProperties.Create($NewTypeProperty)
				$NewSubProperty.DefaultPrivacy = [Microsoft.Office.Server.UserProfiles.Privacy]::$SharePointPropPrivacy
				$NewSubProperty.PrivacyPolicy = [Microsoft.Office.Server.UserProfiles.PrivacyPolicy]::$SharePointPropPrivacyPolicy
				
				#Finalizing the new property.
				$userProfileProperties.Add($NewSubProperty)
				$err = $userProfileProperties.GetEnumerator()
				Write-Host "    Created user profile property `"$SharePointProp`"."				
			}
			else
			{
				Write-Host "There are no profiles to be created in the parse file."
			}
		}
		
		Write-Host "Finished creating user profile synchronizatoin connection." -ForegroundColor green
		
	    ######################
    	#/Creating properties#
	    ######################
    
	    ############################
    	#Creating property mappings#
	    ############################
    	
		Write-Host "Creating user profile mappings." -ForegroundColor green
		
		$PropertyNodeList = $XmlDoc.Connections.Connection.PropertyMappings

	    foreach ($PropertyNode in $PropertyNodeList) 
    	{
		    if ($PropertyNode)
			{
		        $ProfilePropertyMapping = $PropertyNode.SelectSingleNode("ProfilePropertyMapping")
    		    $SharePointProp = $ProfilePropertyMapping.SharePointProp
        		$direction = $ProfilePropertyMapping.direction.ToLower()
		        $DomainAttribute = $ProfilePropertyMapping.InnerText

				#Checking the direction parameter. It's either import or export.
    		    if ($direction -eq "import")
        		{
					$MappingDirectionMSG = "<="
					#Doing some really crapy error checking as the AddNewMapping method does not give any error. Ever.
					#The idea is to check how long the command ran. If it finishes quick it did not do it.
					$pretime = Get-Date
					$PropertyMapping.AddNewMapping($userprofiletype,$SharePointProp,$DomainAttribute)
					$posttime = Get-Date

					if (($posttime.Minute -eq $pretime.Minute) -and (($posttime.Second-$pretime.Second) -le 2))
					{
						Write-Host "The property mapping of SharePoint property `"$SharePointProp`" to domain attribute `"$DomainAttribute`" has most probably failed. Please check the setting manually. Moving to next item." -ForegroundColor red
					}
					else
					{
						Write-Host "Created user profile mapping `"$SharePointProp`"."
					}
		        }
    		    elseif ($direction -eq "export")
        		{
					$MappingDirectionMSG = "=>"
					#Doing some really crapy error checking as the AddNewMapping method does not give any error. Ever.
					#The idea is to check how long the command ran. If it finishes quick it did not do it.
					$pretime = Get-Date
					$PropertyMapping.AddNewExportMapping($userprofiletype,$SharePointProp,$DomainAttribute)
					$posttime = Get-Date

					if (($posttime.Minute -eq $pretime.Minute) -and (($posttime.Second-$pretime.Second) -le 2))
					{
						Write-Host "The property mapping of SharePoint property `"$SharePointProp`" to domain attribute `"$DomainAttribute`" has most probably failed. Please check the setting manually. Moving to next item." -ForegroundColor red
					}
					else
					{
						Write-Host "    Created mapping `"$SharePointProp`" $MappingDirectionMSG `"$DomainAttribute`"."
					}
        
		        }
    		    else
        		{
            		Write-Host "Invalid operation for 'direction' attribute. Skipping mapping/creation for SharePoint property `"$SharePointProp`"." -ForegroundColor red
		            Continue
    		    }
        	}
	    }

		Write-Host "Finished creating user profile mappings." -ForegroundColor green
		
	    #############################
    	#/Creating property mappings#
    	#############################

		Write-Host "Finished creating user profile synchronization connection `"$SyncConnectionName`"." -ForegroundColor green

    }
	else
	{
		Write-Host "There's no synchronization connection specified. Aborting." -ForegroundColor red
	}
}

